import { FilterOption } from './model-options';

/**
 * 树状节点接口
 */
interface TreeNode<V> {
  children: Map<string, TreeNode<V>>;
  element?: V;
}

/**
 * 通用树状存储map
 */
export class TreeMap<T, K extends object> {
  /**
   * 树状MAP根节点
   *
   * @type {TreeNode<T[]>}
   * @private
   */
  private readonly root: TreeNode<T> = { children: new Map() };

  /**
   * 组合键的属性值列表
   *
   * @type {string[]}
   * @private
   */
  private readonly properties: string[];

  /**
   * 构造方法，初始化组合键的属性值列表
   *
   * @param {K} defaultOption
   */
  constructor(defaultOption: K) {
    this.properties = Object.keys(defaultOption);
  }

  /**
   * 根据组合键将元素插入到树状map对应位置
   *
   * @param {K} key
   * @param {T} value
   */
  insert(key: K, value: T) {
    // 将当前节点设置为根节点
    let currentNode = this.root;

    // 遍历组合键的属性值列表
    for (const property of this.properties) {
      // 从组合键中获取当前 组合键属性的值，并将该值作为 key 从当前节点获取对应的子节点
      const propertyVal = String((key as any)[property]);
      if (!currentNode.children.has(propertyVal)) {
        currentNode.children.set(propertyVal, { children: new Map() });
      }
      currentNode = currentNode.children.get(propertyVal)!;
    }
    currentNode.element = value;
  }

  /**
   * 通过组合键的值找出对应元素
   *
   * @param {K} key
   * @return {T | undefined}
   */
  find(key: K): T | undefined {
    let currentNode = this.root;
    for (const property of this.properties) {
      const propertyVal = String((key as any)[property]);
      if (!currentNode.children.has(propertyVal)) {
        return undefined;
      }
      currentNode = currentNode.children.get(propertyVal)!;
    }
    return currentNode.element;
  }
}

/**
 * 通用树状存储map
 */
export class TreeMultiMap<T, K extends object> {
  /**
   * 树状MAP根节点
   *
   * @type {TreeNode<T[]>}
   * @private
   */
  private readonly root: TreeNode<T[]> = { children: new Map() };

  /**
   * 组合键的属性值列表
   *
   * @type {string[]}
   * @private
   */
  private readonly properties: string[];

  /**
   * 构造方法，初始化组合键的属性值列表
   *
   * @param {K} defaultOption
   */
  constructor(defaultOption: K) {
    this.properties = Object.keys(defaultOption);
  }

  /**
   * 插入元素
   *
   * @param {K} key
   * @param {T} value
   */
  insert(key: K, value: T) {
    let currentNode = this.root;
    for (const property of this.properties) {
      const propertyVal = String((key as any)[property] ?? 'default');
      if (!currentNode.children.has(propertyVal)) {
        currentNode.children.set(propertyVal, { children: new Map() });
      }
      currentNode = currentNode.children.get(propertyVal)!;
    }
    if (currentNode.element === undefined) {
      currentNode.element = [];
    }
    currentNode.element!.push(value);
  }

  /**
   * 查找元素
   *
   * @param {Partial<K> | FilterOption<K>} filter
   * @return {T[]}
   */
  find(filter?: Partial<K> | FilterOption<K>): T[] {
    const result: T[] = [];
    this.findElements(filter ?? {}, this.root, 0, result);
    return result;
  }

  /**
   * 递归查找对应元素
   *
   * @param {Partial<K> | FilterOption<K>} filter
   * @param {TreeNode<T[]>} currentNode
   * @param {number} propertyIndex
   * @param {T[]} result
   * @private
   */
  private findElements(filter: Partial<K> | FilterOption<K>, currentNode: TreeNode<T[]>, propertyIndex: number, result: T[]) {
    // 过滤已完成 或 子节点为空，终止递归
    if (propertyIndex === this.properties.length || currentNode.children.size === 0) {
      if (currentNode.element !== undefined && currentNode.element.length > 0) {
        result.push(...currentNode.element);
      }
      return;
    }

    // 获取条件键值中 当前要过滤的属性 的值
    const filterVal = (filter as any)[this.properties[propertyIndex]];
    if (filterVal === undefined) {
      // 如果不存在，则表示不过滤该条件，遍历当前节点全部子节点进行下一个条件过滤
      for (const childNode of currentNode.children.values()) {
        this.findElements(filter, childNode, propertyIndex + 1, result);
      }
    } else {
      if (Array.isArray(filterVal)) {
        for (const [nodeKey, childNode] of currentNode.children.entries()) {
          if (filterVal.includes(nodeKey)) {
            this.findElements(filter, childNode, propertyIndex + 1, result);
          }
        }
      } else if (typeof filterVal === 'function') {
        // 通过函数过滤
        const filterFun = filterVal as (val: string) => boolean;
        for (const [nodeKey, childNode] of currentNode.children.entries()) {
          if (filterFun(nodeKey)) {
            this.findElements(filter, childNode, propertyIndex + 1, result);
          }
        }
      } else {
        // 通过属性类型过滤
        const nodeKey = String(filterVal);
        if (!currentNode.children.has(nodeKey)) {
          return;
        }
        this.findElements(filter, currentNode.children.get(nodeKey)!, propertyIndex + 1, result);
      }
    }
  }
}
